﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;

namespace Xceed.Silverlight.Data.Stats
{
  public class CorrelationCoefficientFunction : StatFunction
  {
    public CorrelationCoefficientFunction()
      : base()
    {
    }

    public CorrelationCoefficientFunction( string resultPropertyName, string sourcePropertyName )
      : base( resultPropertyName, sourcePropertyName )
    {
    }

    #region PrerequisiteFunctions Property

    protected internal override StatFunction[] PrerequisiteFunctions
    {
      get
      {
        StatFunction[] prerequisites = m_prerequisites;

        if( prerequisites == null )
        {
          prerequisites = new StatFunction[]
          {
            new PreCovarianceFunction( "CorrelationCoefficientFunction.PreCovariance", this.SourcePropertyName )
          };

          if( this.IsSealed )
            m_prerequisites = prerequisites;
        }

        return prerequisites;
      }
    }

    #endregion

    protected internal override void Validate()
    {
      base.Validate();
      this.ValidateSourcePropertyName( 2 );
    }

    protected override void Initialize( Type[] sourcePropertyTypes )
    {
      base.Initialize( sourcePropertyTypes );

      m_accumulationType = TypeCode.Double;

      for( int i = 0; i < sourcePropertyTypes.Length; i++ )
      {
        if( sourcePropertyTypes[ i ] == typeof( decimal ) )
        {
          m_accumulationType = TypeCode.Decimal;
          break;
        }
      }
    }

    protected internal override void Reset()
    {
      m_avgXDouble = 0d;
      m_avgYDouble = 0d;
      m_avgXDecimal = 0m;
      m_avgYDecimal = 0m;
      m_count = 0;

      m_sumCovDouble = 0d;
      m_sumCovDecimal = 0m;
      m_sumXVarianceDouble = 0d;
      m_sumYVarianceDouble = 0d;
      m_sumXVarianceDecimal = 0m;
      m_sumYVarianceDecimal = 0m;
    }

    protected internal override void InitializePrerequisites( StatResult[] prerequisitesValues )
    {
      PreCovarianceResult preResult = ( PreCovarianceResult )prerequisitesValues[ 0 ];

      switch( m_accumulationType )
      {
        case TypeCode.Double:
          {
            m_avgXDouble = Convert.ToDouble( preResult.AvgX );
            m_avgYDouble = Convert.ToDouble( preResult.AvgY );
          }
          break;
        case TypeCode.Decimal:
          {
            m_avgXDecimal = Convert.ToDecimal( preResult.AvgX );
            m_avgYDecimal = Convert.ToDecimal( preResult.AvgY );
          }
          break;
      }

      m_count = preResult.Count;
    }

    protected internal override void Accumulate( object[] values )
    {
      if( StatFunction.CanProcessValues( values, 2 ) )
      {
        checked
        {
          switch( m_accumulationType )
          {
            case TypeCode.Double:
              {
                double diffX = Convert.ToDouble( values[ 0 ] ) - m_avgXDouble;
                double diffY = Convert.ToDouble( values[ 1 ] ) - m_avgYDouble;

                m_sumXVarianceDouble += diffX * diffX;
                m_sumYVarianceDouble += diffY * diffY;
                m_sumCovDouble += diffX * diffY / m_count;
              }
              break;

            case TypeCode.Decimal:
              {
                decimal diffX = Convert.ToDecimal( values[ 0 ] ) - m_avgXDecimal;
                decimal diffY = Convert.ToDecimal( values[ 1 ] ) - m_avgYDecimal;

                m_sumXVarianceDecimal += diffX * diffX;
                m_sumYVarianceDecimal += diffY * diffY;
                m_sumCovDecimal += diffX * diffY / m_count;
              }
              break;
          }
        }
      }
    }

    protected internal override StatResult GetResult()
    {
      object result = null;

      checked
      {
        switch( m_accumulationType )
        {
          case TypeCode.Double:
            {
              double denominator = Math.Sqrt( m_sumXVarianceDouble / m_count ) * Math.Sqrt( m_sumYVarianceDouble / m_count );

              if( denominator == 0d )
                throw new DivideByZeroException();

              result = m_sumCovDouble / denominator;
            }
            break;

          case TypeCode.Decimal:
            {
              double denominator = Math.Sqrt( ( double )m_sumXVarianceDecimal / m_count ) * Math.Sqrt( ( double )m_sumYVarianceDecimal / m_count );

              if( denominator == 0d )
                throw new DivideByZeroException();

              result = ( double )m_sumCovDecimal / denominator;
            }
            break;
        }
      }

      return new StatResult( result );
    }

    private StatFunction[] m_prerequisites; // = null
    private TypeCode m_accumulationType = TypeCode.Empty;

    // Prerequisite variables
    private long m_count;
    private double m_avgXDouble;
    private decimal m_avgXDecimal;
    private double m_avgYDouble;
    private decimal m_avgYDecimal;

    // Accumulation variables
    private double m_sumCovDouble;
    private decimal m_sumCovDecimal;
    private double m_sumXVarianceDouble;
    private double m_sumYVarianceDouble;
    private decimal m_sumXVarianceDecimal;
    private decimal m_sumYVarianceDecimal;
  }
}
